package com.smsc.headend.connector.manager;


import cn.hutool.core.lang.Tuple;
import cn.hutool.core.map.MapUtil;
import cn.hutool.json.JSONUtil;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Lists;
import com.smsc.headend.common.enums.DeviceType;
import com.smsc.headend.common.enums.KmsActionType;
import com.smsc.headend.common.utils.R;
import com.smsc.headend.common.utils.RedisUtils;
import com.smsc.headend.connector.builder.LoggerBuilder;
import com.smsc.headend.connector.service.DeviceService;
import com.smsc.headend.connector.service.FeignAssetManagementService;
import com.smsc.headend.connector.service.asset.AssetService;
import com.smsc.headend.connector.utils.LogFileNameUtil;
import com.smsc.headend.module.asset.entity.ComEntity;
import com.smsc.headend.module.asset.entity.DeviceTypeGroup;
import com.smsc.headend.module.asset.entity.Meter;
import com.smsc.headend.module.asset.entity.SecurityHls;
import com.smsc.headend.module.data.dmo.DeviceSAPandKeys;
import com.smsc.headend.module.task.dto.ConnectorTask;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

@Component
public class DeviceKeyManager {
    @Autowired
    FeignAssetManagementService assetManagementRemoteService;
    @Autowired
    LogFileNameUtil logFileNameUtil;
    @Autowired
    AssetService assetService;
    @Autowired
    RedisUtils redisUtils;
    @Autowired
    DeviceService deviceService;

    /**
     * 设备缓存   Key  Tuple[]  key[0] deviceId,  key[1] roleId
     */
    Cache<Tuple, DeviceSAPandKeys> deviceKeyCached = CacheBuilder.newBuilder().build();

    public void loadDefaultComKey(ComEntity comEntity) {
        Tuple tuple = new Tuple(comEntity.getDeviceId(), 1L);
        DeviceSAPandKeys keys = deviceKeyCached.getIfPresent(tuple);
        if (keys != null) {
            return;
        }

        String kmsEnabled = assetService.getSystemProperty("KMS_ENABLE");
        if ("True".equalsIgnoreCase(kmsEnabled)) {
            return;
        }

        if (Optional.ofNullable(comEntity.getComDeviceType()).orElse(0L).longValue() == DeviceType.GPRS.getId().longValue()) {
            Meter meter = new Meter();
            meter.setMeterId(comEntity.getDeviceId());
            keys = getInnerDeviceKeys(meter, comEntity, 1);//线的RoleId
        } else if (Optional.ofNullable(comEntity.getComDeviceType()).orElse(0L).longValue() == DeviceType.DCU.getId().longValue()) {
            keys = getInnerDeviceKeys(null, comEntity, 1);//线的RoleId
        }

        if (keys != null) {
            LoggerBuilder.getLogger(logFileNameUtil.getLogFileName(comEntity), this.getClass()).info("defaultKeyLoaded");
            deviceKeyCached.put(tuple, keys);
        }
    }

    /**
     * 获取设备密钥，
     * 如果配置了KMS系统，从KMS系统接口获取设备的EK,AK
     *
     * @param meter
     * @param terminal
     * @param connectorTask
     * @return
     */
    public DeviceSAPandKeys getDeviceKeys(Meter meter, ComEntity terminal, ConnectorTask connectorTask) {
        DeviceSAPandKeys deviceSAPandKeys = getFromLocalCache(meter, terminal, connectorTask);
        if (deviceSAPandKeys != null) {
            return deviceSAPandKeys;
        }

        LoggerBuilder.getLogger(logFileNameUtil.getLogFileName(terminal), this.getClass()).info("AA getDeviceKeys: comId={},meter={}, roleId={}", terminal.getComId(), meter == null ? null : meter.getMeterId(), connectorTask.getRoleId());
        String kmsEnabled = assetService.getSystemProperty("KMS_ENABLE");

        if ("True".equalsIgnoreCase(kmsEnabled)) {
            deviceSAPandKeys = getDeviceKeysFromKMS(meter, terminal, connectorTask);

        } else {
            deviceSAPandKeys = getInnerDeviceKeys(meter, terminal, connectorTask.getRoleId());
        }


        addToCache(meter, terminal, connectorTask, deviceSAPandKeys);
        return deviceSAPandKeys;
    }

    private void addToCache(Meter meter, ComEntity terminal, ConnectorTask connectorTask, DeviceSAPandKeys deviceSAPandKeys) {
        Tuple key = new Tuple(meter == null ? terminal.getDeviceId() : meter.getMeterId(), connectorTask.getRoleId());
        deviceKeyCached.put(key, deviceSAPandKeys);
    }

    private DeviceSAPandKeys getFromLocalCache(Meter meter, ComEntity terminal, ConnectorTask connectorTask) {
        Tuple key = new Tuple(meter == null ? terminal.getDeviceId() : meter.getMeterId(), connectorTask.getRoleId());
        return deviceKeyCached.getIfPresent(key);
    }

    private DeviceSAPandKeys getDeviceKeysFromKMS(Meter meter, ComEntity terminal, ConnectorTask connectorTask) {
        Map<String, Object> kmsRequestParam = new HashMap<>();

        Long modelId = deviceService.getModelId(terminal, meter);
        DeviceTypeGroup deviceTypeGroup = redisUtils.getDeviceTypeGroup(modelId, null, assetManagementRemoteService);
        kmsRequestParam.put("serialNumber", meter == null ? assetService.getDeviceCustomerNo(terminal.getDeviceId(), terminal.getComDeviceType()) : assetService.getDeviceCustomerNo(meter.getMeterId(), DeviceType.METER.getId()));
        kmsRequestParam.put("logicObjectName", assetManagementRemoteService.getDeviceRoleName(connectorTask.getRoleId().longValue()));
        kmsRequestParam.put("manufacture", deviceTypeGroup.getManufacturerName());
        kmsRequestParam.put("className", deviceTypeGroup.getModelName());
        String bizContent = JSONUtil.toJsonStr(kmsRequestParam);

        LoggerBuilder.getLogger(logFileNameUtil.getLogFileName(terminal), this.getClass()).info("calling kms for device key: meterId={}, roleId={}", meter == null ? null : meter.getMeterId(), connectorTask.getRoleId());
        R r = assetManagementRemoteService.sendToKms(KmsActionType.GetSecurityKeys.name(), bizContent);
        LoggerBuilder.getLogger(logFileNameUtil.getLogFileName(terminal), this.getClass()).info("complete kms for device key: meterId={}, roleId={}, result={}", meter == null ? null : meter.getMeterId(), connectorTask.getRoleId(), JSONUtil.toJsonStr(r));
        return convertKMSResponse(meter, terminal, connectorTask, JSONUtil.toJsonStr(r));
    }

    private DeviceSAPandKeys convertKMSResponse(Meter meter, ComEntity terminal, ConnectorTask connectorTask, String result) {
        R ret = JSONUtil.toBean(result, R.class);
        if (ret.isOk()) {
            DeviceSAPandKeys deviceSAPandKeys = getInnerDeviceKeys(meter, terminal, connectorTask.getRoleId());
            String bizContent = ret.get("bizContent").toString();
            Map<String, String> bizContentMap = JSONUtil.toBean(bizContent, Map.class);
            SecurityHls securityHls = new SecurityHls();
            securityHls.setGuek(MapUtil.getStr(bizContentMap, "guek"));
            securityHls.setGak(MapUtil.getStr(bizContentMap, "gak"));

            if (StringUtils.isEmpty(securityHls.getGuek()) && StringUtils.isEmpty(securityHls.getGak())) {
                LoggerBuilder.getLogger(logFileNameUtil.getLogFileName(terminal), this.getClass()).error("kms key incorrect: meterId={}, roleId={}, securityHls={}", meter == null ? null : meter.getMeterId(), connectorTask.getRoleId(), securityHls);
            }
            deviceSAPandKeys.setHlsKeys(Lists.newArrayList(securityHls));
            return deviceSAPandKeys;
        }
        return null;
    }

    private DeviceSAPandKeys getInnerDeviceKeys(Meter meter, ComEntity terminal, Integer roleId) {
        if (meter == null) {
            return assetManagementRemoteService.getTerminalCommunicationKey(terminal.getComId(), roleId);
        } else {
            return assetManagementRemoteService.getMeterCommunicationKey(meter.getMeterId(), roleId);
        }
    }

    public void invalidKey(Long deviceId) {
        deviceKeyCached.invalidate(deviceId);
    }
}
